from neo4j import GraphDatabase
import time
import json
from loguru import logger
from functools import wraps


def used_time(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()  # 记录开始时间
        result = func(*args, **kwargs)  # 调用原函数
        end_time = time.time()  # 记录结束时间
        elapsed_time = end_time - start_time  # 计算经过的时间
        logger.info(f"Function {func.__name__} executed in {elapsed_time:.4f} seconds")
        return result

    return wrapper


class Neo4jServices:

    # 对外的接口，由Web服务进行调用转化为Rest API
    def __init__(self, uri, user, password, db_name='neo4j'):
        self.driver = GraphDatabase.driver(uri, auth=(user, password))
        self.db_name = db_name

    def close(self):
        self.driver.close()

    # 创建节点
    def create_node(self, label, data):
        with self.driver.session() as session:
            # 构建创建节点的 Cypher 查询
            query = (
                     f"MERGE (node:{label} {{"
                     + ', '.join([f"{key}: ${key}" for key in data.keys()])
                     + "})"
            )
            # 执行查询
            #logger.debug(query)
            session.run(query, parameters=data)

    # 创建节点，设置多个标签
    def create_node_labels(self, labels, data):
        with self.driver.session() as session:
            # 将标签列表转换为字符串形式，例如 "Label1:Label2"
            label_str = ":".join(labels)

            # 构建创建节点的 Cypher 查询
            query = (
                    f"MERGE (node:{label_str} {{"
                    + ', '.join([f"{key}: ${key}" for key in data.keys()])
                    + "})"
            )

            # 执行查询
            session.run(query, parameters=data)

    def set_node_props(self, label,name,data):
        with self.driver.session() as session:
            #query =  f"Match (node:{label}) "+" Where node.name='"+name+"' set node {"
            #         + ', '.join([f"{key}:${key}" for key in data.keys()])+ "}"
            # 执行查询
            query =  "Match (node:"+label+") "+" Where node.name='"+name+"' set node += $data"
            #logger.debug(query)
            session.run(query, data=data)

    def set_node_labels_props(self, labels,name,data):
        with self.driver.session() as session:
            label_str = ":".join(labels)
            query =  "Match (node:"+label_str+") "+" Where node.name='"+name+"' set node += $data"
            #logger.debug(query)
            session.run(query, data=data)

    def create_relationship_between_nodes(self,node1,node2,lable1,lable2,r):
        def work(tx):
            query = "MATCH (a:{}{{name:$node1}}), (b:{}{{name:$node2}}) MERGE (a)-[:{}]->(b)".format(lable1,lable2,r)
            tx.run(query,node1=node1,node2=node2)
            #tx.run("""MATCH (a:技术{name:$node1}), (b:技术{name:$node2}) MERGE (a)-[:{r:$r}]->(b)""",node1=node1,node2=node2,r=r)
        with self.driver.session(database=self.db_name) as session:
            session.execute_write(work)
    
    # 返回某些专利节点IDs(这个node_ids是节点的Neo4j IDs)组成的子图
    def get_all_entities_relationships(self, node_ids, maxLevel='1', limit="-1"):
        query = "CALL apoc.path.subgraphAll(" + str(node_ids) + ",{labelFilter:\"技术|关键词|AI核心词\", maxLevel:" + str(
            maxLevel) + ",limit:" + str(
            limit) + "}) YIELD nodes, relationships RETURN [node in nodes | [ elementId(node),node.name,labels(node),node._id_elasticsearch,node._index_elasticsearch]] as nodes ,[rel in relationships | [elementId(rel),type(rel),startNode(rel).name,endNode(rel).name,elementId(startNode(rel)),elementId(endNode(rel)),labels(startNode(rel)),labels(endNode(rel)) ]] as relationships ;"
        logger.debug("query : {}".format(query))
        result = []
        nodes = []
        original_nodes_ids = []
        relationships = []
        with self.driver.session() as session:
            result = session.run(query)
            for record in result:
                for node in (record[0]):
                    add_node = {}
                    add_node['nodeId'] = node[0]
                    add_node['name'] = node[1]
                    add_node['category'] = node[2][0]
                    add_node['_id_elasticsearch'] = node[3]
                    add_node['_index_elasticsearch'] = node[4]
                    nodes.append(add_node)
                    if add_node['_id_elasticsearch']:
                        original_nodes_ids.append(add_node['_id_elasticsearch'])
                for relationship in (record[1]):
                    add_relationship = {}
                    add_relationship['nodeId'] = relationship[0]
                    add_relationship['name'] = relationship[1]
                    add_relationship['source'] = relationship[2]
                    add_relationship['target'] = relationship[3]
                    add_relationship['sourceID'] = relationship[4]
                    add_relationship['targetID'] = relationship[5]
                    add_relationship['sourceNodeCategory'] = relationship[6][0]
                    add_relationship['targetNodeCategory'] = relationship[7][0]
                    #if add_relationship['name'] != '相关行业':
                    relationships.append(add_relationship)
        logger.trace("Nodes: {}".format(nodes))
        logger.trace("Relationships: {}".format(relationships))
        logger.trace("original_nodes_ids {}".format(original_nodes_ids))
        return nodes, relationships, original_nodes_ids


    def get_all_entities_relationships_for_graph(self, node_ids, maxLevel='3', limit="-1",industry=False):
        if industry:
            query = "CALL apoc.path.subgraphAll(" + str(node_ids) + ",{labelFilter:\"__Document__|__Entity__|__Chunk__|标题|年份标签|省份|关键词\",relationshipFilter:\"相关实体|PART_OF|HAS_ENTITY|AI涉及|AI来自|专利_AI核心词|专利_关键词|专利_技术|专利_标题|专利_摘要|专利_年份标签|专利_省份|专利_关键词\", maxLevel:" + str(
                    maxLevel) + ",bfs:true, limit:" + str(limit) + "}) YIELD nodes, relationships RETURN [node in nodes | [ elementId(node),node.name,labels(node),node._id_elasticsearch,node._index_elasticsearch,node.type,node.patent_name,node.title,node.description]] as nodes ,[rel in relationships | [elementId(rel),type(rel),startNode(rel).name,endNode(rel).name,elementId(startNode(rel)),elementId(endNode(rel)),labels(startNode(rel)),labels(endNode(rel)),rel.description ]] as relationships ;"
        else:
            query = "CALL apoc.path.subgraphAll(" + str(node_ids) + ",{labelFilter:\"__Document__|__Entity__|__Chunk__|标题|年份标签|省份|关键词\",relationshipFilter:\"相关实体|相关内容|PART_OF|HAS_ENTITY|AI涉及|AI来自|专利_AI核心词|专利_关键词|专利_技术|专利_标题|专利_摘要|专利_年份标签|专利_省份|专利_关键词｜论文_年份标签|论文_标题|论文_省份|论文_关键词|政策_年份标签|政策_标题|政策_省份|政策_关键词|资讯_年份标签|资讯_标题|资讯_省份|资讯_关键词\", maxLevel:" + str(
                maxLevel) + ",limit:" + str(limit) + "}) YIELD nodes, relationships RETURN [node in nodes | [ elementId(node),node.name,labels(node),node._id_elasticsearch,node._index_elasticsearch,node.type,node.patent_name,node.title,node.description]] as nodes ,[rel in relationships | [elementId(rel),type(rel),startNode(rel).name,endNode(rel).name,elementId(startNode(rel)),elementId(endNode(rel)),labels(startNode(rel)),labels(endNode(rel)),rel.description ]] as relationships ;"
            logger.debug("query : {}".format(query))
        logger.trace("query : {}".format(query))
        result = []
        nodes = []
        original_nodes_ids = []
        relationships = []
        with self.driver.session() as session:
            result = session.run(query)
            for record in result:
                for node in (record[0]):
                    add_node = {}
                    add_node['nodeId'] = node[0]
                    add_node['name'] = node[1]
                    add_node['category'] = node[2][0]
                    add_node['_id_elasticsearch'] = node[3]
                    add_node['_index_elasticsearch'] = node[4]
                    add_node['type'] = node[5]
                    add_node['patent_name'] = node[6]
                    add_node['title'] = node[7]
                    add_node['description'] = node[8]
                    if add_node['type'] is None:
                        add_node['type'] = add_node['category']
                    if add_node['category'] =='__Entity__':
                        #使用更细的type替换category
                        add_node['category'] = add_node['type']
                    if add_node['category'] =='__Chunk__':
                        add_node['category'] = '段落'
                        if add_node['name'] is None:
                            if add_node['title']:
                                add_node['name'] = add_node['title']
                            elif add_node['patent_name']:
                                add_node['name'] = add_node['patent_name']
                            else:
                                add_node['name'] = add_node['description']
                    if add_node['category'] =='__Document__':
                        #使用更细的type替换category
                        add_node['category'] = '内容'
                    nodes.append(add_node)
                    #返回相关的专利ID
                    if add_node['_id_elasticsearch']:
                        original_nodes_ids.append(add_node['_id_elasticsearch'])
                for relationship in (record[1]):
                    add_relationship = {}
                    add_relationship['nodeId'] = relationship[0]
                    add_relationship['name'] = relationship[1]
                    if add_relationship['name'] in ['关键词_年份标签','关键词_公开日','关键词_申请日']:
                        logger.trace("关系 {} 被忽略".format(add_relationship['name']))
                        continue
                    add_relationship['source'] = relationship[2]
                    add_relationship['target'] = relationship[3]
                    add_relationship['sourceID'] = relationship[4]
                    add_relationship['targetID'] = relationship[5]
                    add_relationship['sourceNodeCategory'] = relationship[6][0]
                    add_relationship['targetNodeCategory'] = relationship[7][0]
                    add_relationship['description'] = relationship[8]
                    if add_relationship['description']:
                        add_relationship['name']  = add_relationship['description']
                    relationships.append(add_relationship)

        logger.trace("Nodes: {}".format(nodes))
        logger.trace("Relationships: {}".format(relationships))
        logger.trace("original_nodes_ids {}".format(original_nodes_ids))
        return nodes, relationships, original_nodes_ids

    # 返回某些专利IDs(这个IDs是节点的原始IDs,也就是ElasticSearch中的ID，也在Dify里作为元数据存储或者在标题上存储的)组成的子图
    def get_all_entities_relationships_with_es_id(self, es_node_ids, maxLevel="3", limit=-1):
        query_start = time.time()

        query = f"UNWIND {es_node_ids} AS id_ MATCH (n) WHERE (n:`专利` or n:`论文` or n:`政策` or n:`资讯`) and n.internal_id_elasticsearch=toInteger(id_) RETURN elementId(n), labels(n)"
        node_ids = []
        with self.driver.session() as session:
            result = session.run(query)
            # 获取结果
            node_ids = [record[0] for record in result]
            logger.debug('query {} result {} node_ids {}'.format(query,result, node_ids))
        nodes, relationships, es_nodes_ids = self.get_all_entities_relationships_for_graph(node_ids, maxLevel, limit)
        query_end = time.time()
        time_cost = (query_end - query_start) * 1000
        logger.info(
            'Node Ids Length {},maxLevel {},limit {},结果节点长度 {} ,关系长度 {}, 时间cost {}ms'.format(len(node_ids), maxLevel,
                                                                                                  limit, len(nodes),
                                                                                                  len(relationships),
                                                                                                  time_cost))
        logger.trace("Node Ids {}".format(node_ids))
        return nodes, relationships

    # 返回某个行业组成的子图
    def get_all_entities_relationships_with_industry(self, industry_name, patent_top_n=100,maxLevel="1", limit=-1):
        query_start = time.time()
        # 先通过industry_name 查找到top_n的专利
        # 再从top_n专利找到所有的子图
        industry_name_tag = '2014_2023_'+industry_name
        query = f"MATCH (n:`专利`)-[r]-(m:`行业标签_Level001` {{name:'{industry_name}'}}) where n.`{industry_name_tag}`=true return elementId(n), labels(n) order by n.importance_score desc limit {patent_top_n}"
        #query = f"MATCH (n:`行业标签_Level001`) WHERE n.name='{industry_name}' RETURN elementId(n), labels(n)"
        node_ids = []
        with self.driver.session() as session:
            result = session.run(query)
            # 获取结果
            node_ids = [record[0] for record in result]
            logger.debug('获取专利节点长度 {}'.format(len(node_ids)))
            logger.trace('获取专利节点 result {} node_ids {}'.format(result, node_ids))
        nodes, relationships, nodes_ids = self.get_all_entities_relationships_for_graph(node_ids, maxLevel, limit,industry=True)
        query_end = time.time()
        time_cost = (query_end - query_start) * 1000
        logger.info(
            'Node Ids Length {},maxLevel {},limit {},结果节点长度 {} ,关系长度 {}, 时间cost {}ms'.format(len(node_ids), maxLevel,
                                                                                                  limit, len(nodes),
                                                                                                  len(relationships),
                                                                                                  time_cost))
        logger.trace(
            'Node Ids {},maxLevel {},limit {},结果节点长度 {} ,关系长度 {}, 时间cost {}ms'.format(node_ids, maxLevel,
                                                                                                  limit, len(nodes),
                                                                                                  len(relationships),
                                                                                                  time_cost))
        return nodes, relationships

    # 返回某个行业的关键词和核心词中，度最大的topK
    def get_all_entities_relationships_with_industry_for_topK(self, industry_name, patent_top_n=100,maxLevel='1', limit='-1',
                                                              topN_limit='100'):
        query_start = time.time()
        topN = []
        nodes = []
        relationships = []
        nodes, relationships = self.get_all_entities_relationships_with_industry(industry_name, patent_top_n,maxLevel, limit)
        # 查找这个行业节点所有相关的 关键词、技术、核心词节点
        query = f"MATCH (startNode:`行业标签_Level001`) -[r:`AI核心词_行业标签_Level001`|`技术_行业标签_Level001`]- (relatedNode) WHERE startNode.name = '{industry_name}' RETURN DISTINCT relatedNode"
        #query = f"MATCH (startNode:`行业标签_Level001`) -[r:`AI核心词_行业标签_Level001`|`关键词_行业标签_Level001`|`技术_行业标签_Level001`|`专利_行业标签_Level001`]- (relatedNode) WHERE startNode.name = '{industry_name}' and relatedNode.name not in ['实用新型'] RETURN DISTINCT relatedNode"
        logger.debug('query for 行业 {}'.format(query))
        node_ids = []
        with self.driver.session() as session:
            result = session.run(query)
            # 获取结果
            node_ids = [record[0].element_id for record in result]
            logger.debug('获取行业词语节点 node_ids 长度 {}'.format(len(node_ids)))
        tech_node_ids = node_ids
        logger.trace('node_ids {}'.format(node_ids))
        # 查找所有的这些候选节点（关键词、技术、核心词）中，与专利的关系度最多的topN，即热点词语。
        query = f"UNWIND {tech_node_ids} AS nodeId MATCH (n) WHERE elementId(n) = nodeId WITH n, nodeId MATCH (n)-[r:`专利_关键词`|`AI涉及`|`专利_AI核心词`]-(in) WITH nodeId as nodeId,n.name as name ,labels(n) as labels,COUNT(r) AS totalDegrees RETURN nodeId, name,labels,totalDegrees ORDER BY totalDegrees DESC LIMIT {topN_limit}"
        logger.trace('query is {}'.format(query))
        with self.driver.session() as session:
            result_top = session.run(query)
            count_topN = 0
            for i in result_top:
                count_topN = count_topN + 1
                topN_node = {}
                topN_node['nodeId'] = i['nodeId']
                topN_node['name'] = i["name"]
                topN_node['totalDegrees'] = i["totalDegrees"]
                if i["totalDegrees"] >= 1:
                    topN.append(topN_node)
                else:
                    logger.debug("TopN Ignore {} for  totalDegrees {}".format(topN_node, i["totalDegrees"]))
            logger.info("TopN Run Result Length {}".format(count_topN))
        query_end = time.time()
        time_cost = (query_end - query_start) * 1000
        logger.info('topN长度 {}, 时间cost {}ms'.format(len(topN), time_cost))
        # logger.info('节点长度 {} ,关系长度 {}, topN长度 {}, 时间cost {}ms'.format(len(nodes),len(relationships),len(topN),time_cost))
        return nodes, relationships, topN

    # 添加专利知识到图谱
    @used_time
    def add_knowledge_to_graph(self, graph_name, graph_id, row):
        try:
            self.create_node_labels([graph_name, "专利"], {'name': row['patent'], 'graph_id': graph_id})
            # 添加节点，如果有则覆盖
            self.set_node_labels_props([graph_name, "专利"], row['patent'], row)

            self.create_node_labels([graph_name, "专利"], {'name': row['patent'], 'graph_id': graph_id})
            # self.create_relationship_between_nodes(row['patent'], graph_name, "专利", "图谱标签_level001", "专利_图谱标签_level001")

            if row['year']:
                self.create_node("年份标签", {'name': row['year']})
                self.create_relationship_between_nodes(row['patent'], row['year'], graph_name, "年份标签", "专利_年份标签")
            if row['patent_name']:
                self.create_node("标题", {'name': row['patent_name']})
                self.create_relationship_between_nodes(row['patent'], row['patent_name'], graph_name, "标题", "专利_标题")
            if row['application_number']:
                self.create_node("申请号", {'name': row['application_number']})
                self.create_relationship_between_nodes(row['patent'], row['application_number'], graph_name, "申请号", "专利_申请号")
            if row['publication_date']:
                self.create_node("公开日", {'name': row['publication_date']})
                self.create_relationship_between_nodes(row['patent'], row['publication_date'], graph_name, "公开日", "专利_公开日")
            if row['application_date']:
                self.create_node("申请日", {'name': row['application_date']})
                self.create_relationship_between_nodes(row['patent'], row['application_date'], graph_name, "申请日", "专利_申请日")
            if row['patent_category']:
                self.create_node("专利类型", {'name': row['patent_category']})
                self.create_relationship_between_nodes(row['patent'], row['patent_category'], graph_name, "专利类型", "专利_专利类型")
            if row['cp_name']:
                self.create_node("公开国别", {'name': row['cp_name']})
                self.create_relationship_between_nodes(row['patent'], row['cp_name'], graph_name, "公开国别", "专利_公开国别")
            if row['sovereignty_item']:
                self.create_node("权力要求数量", {'name': row['sovereignty_item']})
                self.create_relationship_between_nodes(row['patent'], row['sovereignty_item'], graph_name, "权力要求数量","专利_权力要求数量")
            if row['subject_code']:
                self.create_node("IPC", {'name': row['subject_code']})
                self.create_relationship_between_nodes(row['patent'], row['subject_code'], graph_name, "IPC", "专利_IPC")
            if row['industry']:
                self.create_node("国民经济类别", {'name': row['industry']})
                self.create_relationship_between_nodes(row['patent'], row['industry'], graph_name, "国民经济类别", "专利_国民经济类别")
            if row['abstract']:
                self.create_node("摘要", {'name': row['abstract']})
                self.create_relationship_between_nodes(row['patent'], row['abstract'], graph_name, "摘要", "专利_摘要")
            if row['province']:
                self.create_node("省份", {'name': row['province']})
                self.create_relationship_between_nodes(row['patent'], row['province'], graph_name, "省份", "专利_省份")

            if row['keywords']:
                for key_tag in row['keywords']:
                    self.create_node("关键词", {'name': key_tag})
                    self.create_relationship_between_nodes(row['patent'], key_tag, graph_name, "关键词", "专利_关键词")
                    if row['publication_date']:
                        self.create_relationship_between_nodes(key_tag, row['publication_date'], "关键词", "公开日", "关键词_公开日")
                    if row['application_date']:
                        self.create_relationship_between_nodes(key_tag, row['application_date'], "关键词", "申请日", "关键词_申请日")
                    if row['year']:
                        self.create_node("年份标签", {'name': row['year']})
                        self.create_relationship_between_nodes(key_tag, row['year'], "关键词", "年份标签", "关键词_年份标签")

            return True
        except Exception as e:
            import traceback
            traceback.print_exc()
            logger.error("Exception {} when doc {}".format(e, json.dumps(row)))
            return None
        finally:
            self.close()

    # 获取图谱构建图谱详情节点数量
    def get_knowledge_graph_node_number(self, graph_name):
        query = f"MATCH (n:`{graph_name}`) -[r:`专利_标题`]- (relatedNode)  OPTIONAL MATCH (n)-[r]-() WITH  COUNT(DISTINCT r) AS total_relations_per_node RETURN  SUM(total_relations_per_node) AS total_relations_all_nodes"
        logger.debug('query for 图谱 {}'.format(query))
        total_relations_all_nodes = 0
        with self.driver.session() as session:
            result = session.run(query)
            # 提取 total_relations_all_nodes 的值
            for record in result:
                total_relations_all_nodes = record["total_relations_all_nodes"]
                break

        return total_relations_all_nodes

    def get_knowledge_graph_relation_number(self, graph_name):
        query = f'''MATCH (n:`{graph_name}`) 
                OPTIONAL MATCH (n)-[r]-()
                WITH 
                    COUNT(DISTINCT r) AS total_relations_per_node
                RETURN 
                    SUM(total_relations_per_node) AS total_relations_number'''

        logger.debug('query for 图谱 {}'.format(query))
        total_relations_number = 0
        with self.driver.session() as session:
            result = session.run(query)
            # 提取 total_relations_all_nodes 的值
            for record in result:
                total_relations_number = record["total_relations_number"]
                break

        return total_relations_number

    # 获取知识图谱指定图谱下节点
    def get_knowledge_graph_relationships(self, graph_name, graph_id, maxLevel="1", limit=-1):
        query_start = time.time()
        # 通过图谱ID 查找到所有的专利
        # 再从专利找到所有的图谱
        query = f"MATCH (n:`{graph_name}`) WHERE n.graph_id={graph_id} RETURN elementId(n), labels(n)"
        logger.debug("node_ids query : {}".format(query))
        # node_ids = ['4:cc2b7dbe-68a5-4d7d-822a-bb3e3e9b9381:53522']
        with self.driver.session() as session:
            result = session.run(query)
            # 获取结果
            node_ids = [record[0] for record in result]
        nodes, relationships, nodes_ids = self.get_knowledge_graph_all_relationships(node_ids, maxLevel, limit)
        return nodes, relationships

    # 获取知识图谱指定图谱下节点所有关系
    def get_knowledge_graph_all_relationships(self, node_ids, maxLevel, limit):
        query = "CALL apoc.path.subgraphAll(" + str(node_ids) + ",{labelFilter:\"\", maxLevel:" + str(
            maxLevel) + ",limit:" + str(
            limit) + "}) YIELD nodes, relationships WITH DISTINCT nodes, relationships RETURN [node in nodes | [ elementId(node),node.name,node.node.graph_id,labels(node)]] as nodes ,[rel in relationships | [elementId(rel),type(rel),startNode(rel).name,endNode(rel).name,elementId(startNode(rel)),elementId(endNode(rel)),labels(startNode(rel)),labels(endNode(rel)) ]] as relationships ;"
        logger.debug("query : {}".format(query))
        result = []
        nodes = []
        original_nodes_ids = []
        relationships = []
        with self.driver.session() as session:
            result = session.run(query)
            for record in result:
                for node in (record[0]):
                    add_node = {}
                    add_node['nodeId'] = node[0]
                    add_node['name'] = node[1]
                    add_node['graph_id'] = node[2]
                    add_node['category'] = node[3][1] if len(node) > 3 and len(node[3]) > 1 else node[3][0]
                    # add_node['_id_elasticsearch'] = node[3]
                    # add_node['_index_elasticsearch'] = node[4]
                    nodes.append(add_node)
                    # if add_node['_id_elasticsearch']:
                    #     original_nodes_ids.append(add_node['_id_elasticsearch'])
                for relationship in (record[1]):
                    add_relationship = {}
                    add_relationship['nodeId'] = relationship[0]
                    add_relationship['name'] = relationship[1]
                    add_relationship['source'] = relationship[2]
                    add_relationship['target'] = relationship[3]
                    add_relationship['sourceID'] = relationship[4]
                    add_relationship['targetID'] = relationship[5]
                    add_relationship['sourceNodeCategory'] = relationship[6][1] if len(relationship) > 6 and len(relationship[6]) > 1 else relationship[6][0]
                    add_relationship['targetNodeCategory'] = relationship[7][1] if len(relationship) > 7 and len(relationship[7]) > 1 else relationship[7][0]
                    relationships.append(add_relationship)
        logger.trace("Nodes: {}".format(nodes))
        logger.trace("Relationships: {}".format(relationships))
        logger.trace("original_nodes_ids {}".format(original_nodes_ids))
        return nodes, relationships, original_nodes_ids


    def get_patent_graph(self, internal_id_elasticsearch=None, patent_name=None, neo4j_label=None):
        with self.driver.session(database=self.db_name) as session:
            # 构建查询条件
            query_parts = []
            if internal_id_elasticsearch:
                query_parts.append(f"p._id_elasticsearch = '{internal_id_elasticsearch}'")
            if patent_name:
                query_parts.append(f"p.name = '{patent_name}'")
            if neo4j_label:
                query_parts.append(f"p:{neo4j_label}")

            if not query_parts:
                raise ValueError("必须提供至少一个过滤条件")

            # 更新查询以返回节点和关系两边的信息
            query = f"""
            MATCH (p)
            WHERE {' AND '.join(query_parts)}
            OPTIONAL MATCH (p)-[r]->(n)
            WHERE type(r) IN ['专利_关键词', 'AI核心词', 'AI来自', 'AI涉及']
            AND (n:关键词 OR n:AI核心词 OR n:技术)
            RETURN
                p AS patent,
                r AS relationship,
                n AS related_node
            """
            result = session.run(query)
            records = []
            for record in result:
                patent_data = dict(record["patent"].items())
                relationship_data = dict(record["relationship"].items())
                related_node_data = dict(record["related_node"].items()) if record["related_node"] else None

                records.append({
                    "patent": patent_data,
                    "relationship": relationship_data,
                    "related_node": related_node_data
                })
            return records
